#pragma once

#include <iostream>
#include <queue>
#include <mutex>
#include <pthread.h>
#include "lockguard.hpp"
const int gDefaultCap = 5;

template <class T>
class BlockQueue
{
private:
    bool isQueueEmpty()
    {
        return bq_.size() == 0;
    }

    bool isQueueFull()
    {
        return bq_.size() == capacity_;
    }

public:
    BlockQueue(int capacity = gDefaultCap) : capacity_(capacity)
    {
        pthread_mutex_init(&mtx_, nullptr);
        pthread_cond_init(&Empty_, nullptr);
        pthread_cond_init(&Full_, nullptr);
    }
    void push(const T &in)
    {
        lockguard lockguard(&mtx_);
        // pthread_mutex_lock(&mtx_);
        //  if (isQueueFull())
        //  {
        //      pthread_cond_wait(&Full_, &mtx_);
        //  }
        while (isQueueFull())
        {
            pthread_cond_wait(&Full_, &mtx_);
        }
        bq_.push(in);
        if (bq_.size() >= capacity_ / 2)
        {
            pthread_cond_signal(&Empty_);
        }
        // pthread_mutex_unlock(&mtx_);
    }

    void pop(T *out)
    {
        lockguard lockguard(&mtx_);
        // pthread_mutex_lock(&mtx_);
        //  if (isQueueEmpty())
        //  {
        //      pthread_cond_wait(&Empty_, &mtx_);
        //  }
        while (isQueueEmpty())
        {
            pthread_cond_wait(&Empty_, &mtx_);
        }
        *out = bq_.front();
        bq_.pop();
        pthread_cond_signal(&Full_);
        // pthread_mutex_unlock(&mtx_);
    }

    ~BlockQueue()
    {
        pthread_mutex_destroy(&mtx_);
        pthread_cond_destroy(&Empty_);
        pthread_cond_destroy(&Full_);
    }

private:
    std::queue<T> bq_;
    int capacity_;
    pthread_mutex_t mtx_;
    pthread_cond_t Empty_;
    pthread_cond_t Full_;
};